package org.railage.server;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.railage.common.dto.PassengerDTO;
import org.railage.common.dto.ScheduleRecordDTO;
import org.railage.common.dto.StationOfRouteDTO;
import org.railage.common.dto.TicketOfUserDTO;
import org.railage.common.dto.TripDTO;
import org.railage.common.dto.TripThroughTwoStationsDTO;
import org.railage.common.dto.UserDTO;
import org.railage.server.dao.PassengerDAO;
import org.railage.server.dao.PassengerDAOImpl;
import org.railage.server.dao.RouteDAO;
import org.railage.server.dao.RouteDAOImpl;
import org.railage.server.dao.ScheduleRecordDAO;
import org.railage.server.dao.ScheduleRecordDAOImpl;
import org.railage.server.dao.StationDAO;
import org.railage.server.dao.StationDAOImpl;
import org.railage.server.dao.StationOfRouteDAO;
import org.railage.server.dao.StationOfRouteDAOImpl;
import org.railage.server.dao.TicketDAO;
import org.railage.server.dao.TicketDAOImpl;
import org.railage.server.dao.TicketOfUserDAO;
import org.railage.server.dao.TicketOfUserDAOImpl;
import org.railage.server.dao.TripDAO;
import org.railage.server.dao.TripDAOImpl;
import org.railage.server.dao.TripThroughTwoStationsDAO;
import org.railage.server.dao.TripThroughTwoStationsDAOImpl;
import org.railage.server.dao.UserDAO;
import org.railage.server.dao.UserDAOImpl;
import org.railage.server.entities.Passenger;
import org.railage.server.entities.Route;
import org.railage.server.entities.ScheduleRecord;
import org.railage.server.entities.Station;
import org.railage.server.entities.StationOfRoute;
import org.railage.server.entities.TicketOfUser;
import org.railage.server.entities.Trip;
import org.railage.server.entities.TripThroughTwoStations;
import org.railage.server.entities.User;
import org.railage.server.util.Utils;

/**
 * Class implementing business logic, which is not implemented in DB stored
 * procedures. Class works with objects' DAOs.
 * 
 * @author Cyril Fertikov
 * 
 */
public class Service {

	private static final Logger log = Logger.getLogger(Service.class);

	private static final String DB_SESSION_ERROR_MSG = "Error in database connection occured";

	private PassengerDAO passengerDAO = new PassengerDAOImpl();
	private RouteDAO routeDAO = new RouteDAOImpl();
	private ScheduleRecordDAO scheduleRecordDAO = new ScheduleRecordDAOImpl();
	private StationDAO stationDAO = new StationDAOImpl();
	private StationOfRouteDAO stationOfRouteDAO = new StationOfRouteDAOImpl();
	private TicketDAO ticketDAO = new TicketDAOImpl();
	private TicketOfUserDAO ticketOfUserDAO = new TicketOfUserDAOImpl();
	private TripDAO tripDAO = new TripDAOImpl();
	private TripThroughTwoStationsDAO tripThroughTwoStationsDAO = new TripThroughTwoStationsDAOImpl();
	private UserDAO userDAO = new UserDAOImpl();

	/*
	 * Operations with users
	 */

	public UserDTO authenticate(Object obj) {
		String[] userdata = (String[]) obj;
		User user = userDAO.getUserByLoginPassword(userdata[0], userdata[1]);
		if (user == null) {
			return null;
		} else {
			return user.toDTO();
		}
	}

	/*
	 * Operations with tickets
	 */

	public String buyTicket(int userID, int tripID, String stationA,
			String stationB) {
		int result = ticketDAO.sellTicket(userID, tripID, stationA, stationB);
		String resultMsg = null;
		switch (result) {
		case 0:
			resultMsg = "Ticket successfully bought!";
			break;
		case 1:
			resultMsg = "No available seats!";
			break;
		case 2:
		case 3:
		case 6:
		case 7:
			resultMsg = "You are already registered on this trip!";
			break;
		case 4:
		case 5:
			resultMsg = "There is less than 10 minutes before start!";
			break;
		case -1:
			resultMsg = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return resultMsg;
	}

	public String buyTicket(Object obj) {
		Object[] params = (Object[]) obj;
		return buyTicket((Integer) params[0], (Integer) params[1],
				(String) params[2], (String) params[3]);
	}

	public String sellTicket(Object obj) {
		Object[] params = (Object[]) obj;

		String surname = (String) params[0];
		String name = (String) params[1];
		String birthday = (String) params[2];
		int tripID = (Integer) params[3];
		String stationA = (String) params[4];
		String stationB = (String) params[5];

		int userID = -1;
		User user = userDAO.getUserByPersonalInfo(surname, name, birthday);
		if (user != null) {
			userID = user.getId();
		} else {
			try {
				user = new User();
				user.setRole(0);
				user.setLogin(surname.toLowerCase());
				user.setPassword(Utils.passwordGen(8));
				user.setSurname(surname);
				user.setName(name);
				user.setBirthday(new SimpleDateFormat("yyyy-MM-dd")
						.parse(birthday));
				userID = userDAO.addUser(user);
			} catch (Exception e) {
				log.error("Exception occured: ", e);
			}
		}
		if (userID != -1) {
			return buyTicket(userID, tripID, stationA, stationB);
		} else {
			return DB_SESSION_ERROR_MSG;
		}
	}

	/*
	 * Operations with routes
	 */

	public String addRoute(Object obj) {
		Object[] params = (Object[]) obj;

		String routeName = (String) params[0];
		String[] stationsArr = (String[]) params[1];
		String[] timesAfterPrevArr = (String[]) params[2];
		String[] timesOnStationsArr = (String[]) params[3];

		String stations = Utils.implode(",", stationsArr);
		String timesAfterPrev = Utils.implode(",", timesAfterPrevArr);
		String timesOnStations = Utils.implode(",", timesOnStationsArr);

		String returnValue = null;
		int result = routeDAO.addRoute(routeName, stations, timesAfterPrev,
				timesOnStations);
		switch (result) {
		case 0:
			returnValue = "Route added";
			break;
		case 1:
			returnValue = "Route with given name already exists!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	public String deleteRoute(Object obj) {
		String returnValue = null;
		int result = routeDAO.deleteRoute((String) obj);
		switch (result) {
		case 0:
			returnValue = "Route deleted";
			break;
		case 1:
			returnValue = "There are trips on route! Cannot delete!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	/*
	 * Operations with trips
	 */

	public String addTrip(Object obj) {
		String returnValue = null;
		Object[] params = (Object[]) obj;
		int result = tripDAO.addTrip((String) params[0], (Integer) params[1],
				(String) params[2]);
		switch (result) {
		case 0:
			returnValue = "Trip added";
			break;
		case 1:
			returnValue = "Selected route does not exist!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	public String deleteTrip(Object obj) {
		String returnValue = null;
		int result = tripDAO.deleteTrip((Integer) obj);
		switch (result) {
		case 0:
			returnValue = "Trip deleted";
			break;
		case 1:
			returnValue = "Some tickets on trip are sold and train did not finish!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	/*
	 * Operations with stations
	 */

	public String addStation(Object obj) {
		String returnValue = null;
		int result = stationDAO.addStation((String) obj);
		switch (result) {
		case 0:
			returnValue = "Station added";
			break;
		case 1:
			returnValue = "Station already exists!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	public String deleteStation(Object obj) {
		String returnValue = null;
		int result = stationDAO.deleteStation((String) obj);
		switch (result) {
		case 0:
			returnValue = "Station deleted";
			break;
		case 1:
			returnValue = "Station does not exist";
			break;
		case 2:
			returnValue = "Station is on route. Cannot delete!";
			break;
		case -1:
			returnValue = DB_SESSION_ERROR_MSG;
			break;
		default:
			break;
		}
		return returnValue;
	}

	public StationOfRouteDTO[] getOrderedStationsOfRoute(Object obj) {
		List<StationOfRoute> stationsOfRoute = stationOfRouteDAO
				.getOrderedStationsOfRoute((String) obj);
		StationOfRouteDTO[] stationsOfRouteDTO = new StationOfRouteDTO[stationsOfRoute
				.size()];
		int i = 0;
		for (StationOfRoute stationOfRoute : stationsOfRoute) {
			stationsOfRouteDTO[i] = stationOfRoute.toDTO();
			i++;
		}
		return stationsOfRouteDTO;
	}

	/*
	 * Getting string arrays (for comboBoxes)
	 */

	public String[] getOrderedStationsOfTrip(Object obj) {
		int tripID = (Integer) obj;
		List<Station> stations = stationDAO.getOrderedStationsOfTrip(tripID);
		if (stations.size() == 0) {
			return null;
		} else {
			String[] stationsArr = new String[stations.size()];
			int i = 0;
			for (Station station : stations) {
				stationsArr[i] = station.getName();
				i++;
			}
			return stationsArr;
		}
	}

	public String[] getRouteNames(Object obj) {
		List<Route> routes = routeDAO.getAllRoutes();
		String[] routeNames = new String[routes.size()];
		int i = 0;
		for (Route route : routes) {
			routeNames[i] = route.getName();
			i++;
		}
		Arrays.sort(routeNames);
		return routeNames;
	}

	public String[] getStationNames(Object obj) {
		List<Station> stations = stationDAO.getAllStations();
		String[] stationNames = new String[stations.size()];
		int i = 0;
		for (Station station : stations) {
			stationNames[i] = station.getName();
			i++;
		}
		Arrays.sort(stationNames);
		return stationNames;
	}

	public String[] getTripIDsByRouteName(Object obj) {
		String routeName = (String) obj;
		List<Trip> trips = tripDAO.getTripsByRouteName(routeName);
		String[] tripIDs = new String[trips.size()];
		int i = 0;
		for (Trip trip : trips) {
			tripIDs[i] = String.valueOf(trip.getId());
			i++;
		}
		return tripIDs;
	}

	/*
	 * Getting contents for tables
	 */

	public TripDTO[] getAllTrips(Object obj) {
		List<Trip> trips = tripDAO.getAllTrips();
		TripDTO[] tripsDTO = new TripDTO[trips.size()];
		int i = 0;
		for (Trip trip : trips) {
			tripsDTO[i] = trip.toDTO();
			i++;
		}
		return tripsDTO;
	}

	public PassengerDTO[] getPassengersByTrip(Object obj) {
		List<Passenger> passengers = passengerDAO
				.getPassengersByTrip((Integer) obj);
		PassengerDTO[] passengersDTO = new PassengerDTO[passengers.size()];
		int i = 0;
		for (Passenger passenger : passengers) {
			passengersDTO[i] = passenger.toDTO();
			i++;
		}
		return passengersDTO;
	}

	public ScheduleRecordDTO[] getSchedule(Object obj) {
		List<ScheduleRecord> schedule = scheduleRecordDAO
				.getSchedule((String) obj);
		ScheduleRecordDTO[] scheduleDTO = new ScheduleRecordDTO[schedule.size()];
		int i = 0;
		for (ScheduleRecord record : schedule) {
			scheduleDTO[i] = record.toDTO();
			i++;
		}
		return scheduleDTO;
	}

	public TripThroughTwoStationsDTO[] getTripsThroughTwoStations(Object obj) {
		String[] params = (String[]) obj;
		List<TripThroughTwoStations> trips = null;
		if (params.length == 2) {
			trips = tripThroughTwoStationsDAO.getTripsThroughTwoStations(
					params[0], params[1]);
		} else if (params.length == 3) {
			trips = tripThroughTwoStationsDAO.getTripsThroughTwoStations(
					params[0], params[1], params[2]);
		} else if (params.length == 4) {
			trips = tripThroughTwoStationsDAO.getTripsThroughTwoStations(
					params[0], params[1], params[2], params[3]);
		}
		TripThroughTwoStationsDTO[] tripsDTO = new TripThroughTwoStationsDTO[trips
				.size()];
		int i = 0;
		for (TripThroughTwoStations trip : trips) {
			tripsDTO[i] = trip.toDTO();
			i++;
		}
		return tripsDTO;
	}

	public TicketOfUserDTO[] getAllTicketsOfUser(Object obj) {
		List<TicketOfUser> tickets = ticketOfUserDAO
				.getAllTicketsOfUser((Integer) obj);
		TicketOfUserDTO[] ticketsDTO = new TicketOfUserDTO[tickets.size()];
		int i = 0;
		for (TicketOfUser ticket : tickets) {
			ticketsDTO[i] = ticket.toDTO();
			i++;
		}
		return ticketsDTO;
	}

	/*
	 * Getting special information
	 */

	public String getRouteByTripID(Object obj) {
		return routeDAO.getRouteByTripID((Integer) obj).getName();
	}

	public int getAvailableTicketsNumber(Object obj) {
		String[] params = (String[]) obj;
		return ticketDAO.getAvailableTicketsNumber(Integer.parseInt(params[0]),
				params[1], params[2]);
	}
}
